package players;

import java.util.ArrayList;

import pieces.Piece;


import model.*;

/**
 * This class is a computer player of Chess.
 * @author Sacha Best
 */
public class ComputerPlayer extends Player
{
	/**
	 * Whether or not the computer uses greedy algorithm.
	 */
	private boolean greedy;
	/**
	 * The Computer's set of {@link pieces.Piece}s
	 */
	private ArrayList<Piece> pieces;
	/**
	 * The opponent's color.
	 */
	private int otherColor;
	/**
	 * The number of moves that have bene played by this Computer
	 */
	private int numMoves;

	/**
	 * Creates a new ComputerPlayer object.
	 * @param board - the {@link model.ChessBoard} the computer plays on
	 * @param color - this ComputerPlayer's color.
	 * @param greedy - Whether or not the computer uses greedy algorithm.
	 */
	public ComputerPlayer(ChessBoard board, int color, boolean greedy)
	{
		super(board, color);
		this.greedy = greedy;
		this.numMoves = 0;
		if (color == Player.WHITE)
		{
			pieces = board.getWhitePieces();
			otherColor = Player.BLACK;
		}
		else
		{
			pieces = board.getBlackPieces();
			otherColor = Player.WHITE;
		}
	}
	@Override
	/**
	 * This method makes a move (either dumb or greedy).
	 * @return the result of {@link model.ChessBoard.completeAction}
	 */
	public String[] makeMove(Location from, Location to)
	{
		if (from != null && to != null)
			return board.completeAction(from, to);
		Location[] resolvedMove = null;
		if (greedy)
			resolvedMove = makeSmartMove();
		else
			resolvedMove = makeRandomMove();
		numMoves++;
		return board.completeAction(resolvedMove[0], resolvedMove[1]);
	}
	/**
	 * This method makes a move if the computer is semi-intelligent. Its first priority is to check its opponent.
	 * Then, it takes the {@link pieces.Piece} of highest value, and finally, if there is no other option, it movves
	 * randomly.
	 * @return a Location[] holding the {@link model.Location} moved form and to
	 */
	private Location[] makeSmartMove()
	{
		int knightValue = 0;
		if (numMoves > 10)
			knightValue = Piece.KNIGHT_VALUE;
		else
			knightValue = Piece.KNIGHT_EARLY_VALUE;
		int highestValue = 0;
		Location moveTo = null, moveFrom = null;
		Piece inCheckResult = board.inCheck(color);
		if (inCheckResult != null)
		{
			for (Piece piece:pieces)
			{
				if (piece.getLocation() != null && board.getObjectAt(piece.getLocation()) == piece)
				{
					ArrayList<Location> possibleLocs = board.handleCheck(piece, inCheckResult, (pieces.King) pieces.get(0));
					if (possibleLocs != null && possibleLocs.size() > 0)
						return new Location[] {piece.getLocation(), possibleLocs.get(0)};
				}
			}
		}
		for (Piece piece:pieces)
		{
			if (piece.getLocation() != null && board.getObjectAt(piece.getLocation()) == piece)
			{
				ArrayList<Location> currentMoveLocations = board.getMoveLocations(piece);
				for (Location loc:currentMoveLocations)
				{
					Location previous = piece.getLocation();
					Piece toTake = board.getObjectAt(loc);
					if (board.getObjectAt(piece.getLocation()) == piece && loc != null && board.inBounds(loc))
					{
						board.move(piece.getLocation(), loc);
						if (board.inCheck(otherColor) != null)
						{
							undo(previous, loc, toTake);
							return new Location[] {previous, loc};
						}
						else
							undo(previous, loc, toTake);
					}
				}
			}
		}
		for (Piece piece:pieces)
		{
			if (piece.getLocation() != null && board.getObjectAt(piece.getLocation()) == piece)
			{
				ArrayList<Location> currentMoveLocations = board.getMoveLocations(piece);
				for (Location loc:currentMoveLocations)
				{
					Piece toTake = board.getObjectAt(loc);
					if (piece instanceof pieces.Pawn && (loc.getRow() == 0 || loc.getRow() == 7))
					{
						moveFrom = piece.getLocation();
						moveTo = loc;
					}
					if (toTake != null)
					{
						if (toTake instanceof pieces.Knight && knightValue >= highestValue)
						{
							moveFrom = piece.getLocation();
							moveTo = loc;
						}
						else if (toTake.getValue() >= highestValue)
						{
							moveTo = loc;
							moveFrom = piece.getLocation();
						}
					}	
				}
			}
		}
		if (moveFrom == null)
			return makeRandomMove();
		Location[] returned = {moveFrom, moveTo};
		return returned;
	}
	/**
	 * This method reverses an action taken by the computer. It is only called in determining whether or not a move
	 * will cause a check for the other player.
	 * @param from - the {@link model.Location} moved from.
	 * @param to - the {@link model.Location} moved to.
	 * @param taken - the {@link pieces.Piece} taken.
	 */
	private void undo(Location from, Location to, Piece taken)
	{
		board.move(to, from);
		if (taken != null)
			board.set(taken, to);
	}
	/**
	 * This method maikes a random move
	 * @return a Location[] holding the {@link model.Location} moved form and to
	 */
	private Location[] makeRandomMove()
	{
		Location moveFrom = null, moveTo = null;
		Piece piece = pieces.get((int) (pieces.size() * Math.random()));
		while (piece.getLocation() == null || board.getObjectAt(piece.getLocation()) != piece
				|| (board.getMoveLocations(piece).size() == 0))
			piece = pieces.get((int) (pieces.size() * Math.random()));
		moveFrom = piece.getLocation();
		ArrayList<Location> moveLocs = board.getMoveLocations(piece);
		moveTo = moveLocs.get((int) (Math.random() * moveLocs.size()));
		Location[] returned = {moveFrom, moveTo};
		return returned;
	}
}
